#pragma once
#include <iostream>
#include <sys/epoll.h>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>

using namespace std;

class TcpServer
{
public:
    const static int N = 1024;
    TcpServer(uint16_t port)
        :_port(port), _listensock(-1), _epfd(-1)
    {
        // 创建tcp套接字
        _listensock = socket(AF_INET, SOCK_STREAM, 0);
        if(_listensock < 0)
        {
            cout << "socket error" << endl;
            exit(1);
        }
        cout << "create socket success, _listensock: " << _listensock << endl;
        // 填写结构体信息
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY;
        // 绑定IP和PORT
        int n = bind(_listensock, (struct sockaddr*)&local, sizeof(local));
        if(n < 0)
        {
            cout << "bind error" << endl;
            exit(2);
        }
        cout << "bind success, _listensock: " << _listensock << endl;
        // 监听
        n = listen(_listensock, 16);
        if(n < 0)
        {
            cout << "listen error" << endl;
            exit(2);
        }
        cout << "listen success, _listensock: " << _listensock << endl;

        // 创建epoll模型
        _epfd = epoll_create(128);
        if(_epfd < 0)
        {
            cout << "epoll create error" << endl;
        }

        // _lsitensock——读事件（接收新连接）
        struct epoll_event ev;
        ev.events = EPOLLIN;
        ev.data.fd = _listensock;
        epoll_ctl(_epfd, EPOLL_CTL_ADD, _listensock, &ev);
    }

    void AcceptClient()
    {
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        int sockfd = accept(_listensock, (struct sockaddr*)&peer, &len);
        if(sockfd < 0) return;
        
        // 新连接交给红黑树管理——设置事件
        struct epoll_event ev;
        ev.events = EPOLLIN;
        ev.data.fd = sockfd;
        epoll_ctl(_epfd, EPOLL_CTL_ADD, sockfd, &ev);
        cout << "add sockfd success, sockfd: " << sockfd << endl;
    }

    void Service(int fd)
    {
        char buffer[1024];
        ssize_t n = recv(fd, buffer, sizeof(buffer)-1, 0);
        if(n > 0)
        {
            buffer[n] = 0;
            cout << "client msg: " << buffer << endl;
            string str = "[echo msg]# ";
            str += buffer;
            send(fd, str.c_str(), str.size(), 0);
        }
        else if(n == 0)
        {
            // 删除红黑树节点必须先保证fd的合法性
            // 总之：先epoll_ctl移除，再close
            epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
            close(fd);
            cout << "recv quit" << endl;
        }
        else 
        {
            epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
            close(fd);
            cout << "recv error" << endl;
        }
    }

    // 处理fd就是接收新连接(accept), _listensock已经就绪
    void handler()
    {
        // 不需要跳过，都是有效的
        for(int i=0; i < N; i++)
        {
            // 判断是什么事件, 要先取出fd和事件
            uint32_t event = rev[i].events;
            int fd = rev[i].data.fd;
            // 读事件
            if(event & EPOLLIN)
            {
                if(fd == _listensock)
                {
                    AcceptClient();
                }
                else 
                {
                    Service(fd);
                }
            }
            // 其他事件...
        }
    }
    void Loop()
    {
        while(true)
        {
            int n = epoll_wait(_epfd, rev, N, -1);
            if(n > 0)
            {
                cout << "开始处理事件..." << endl;
                handler();
            }
            else if(n == 0)
            {
                cout << "epoll timeout" << endl;
            }
            else 
            {
                cout << "epoll error" << endl;
            }
        }
    }
    ~TcpServer()
    {
        if (_listensock > -1)
            close(_listensock);
    }

private:
    uint16_t _port;
    int _listensock;

    int _epfd;
    struct epoll_event rev[N];
};
